import { _decorator, debug } from "cc";
import { CONTROLLER_ENUM, DIRECTION_ENUM, ENTITY_STATE_ENUM, EVENT_TYPE, SHAKE_TYPE_ENUM } from "../../Enums";
import EventManager from "../../Runtime/EventManager";
import { PlayerStateMachine } from "./PlayerStateMachine";
import { EntityManager } from "../../Base/EntityManager";
import DataManager from "../../Runtime/DataManager";
import { IEntity } from "../../Levels";
const { ccclass } = _decorator;

@ccclass("PlayerManager")
export class PlayerManager extends EntityManager {
    targetX: number = 0;
    targetY: number = 0;
    isMoving: boolean = false;
    private readonly speed = 1 / 10;

    async init(params: IEntity) {
        this.fsm = this.addComponent(PlayerStateMachine);
        await this.fsm.init();

        super.init(params);

        this.targetX = this.x;
        this.targetY = this.y;

        EventManager.Instance.on(EVENT_TYPE.PLAYER_CTRL, this.inputHandle, this);
        EventManager.Instance.on(EVENT_TYPE.ATTACH_PLAYER, this.onDeath, this);
    }

    protected onDestroy(): void {
        super.onDestroy();
        EventManager.Instance.off(EVENT_TYPE.PLAYER_CTRL, this.inputHandle);
        EventManager.Instance.off(EVENT_TYPE.ATTACH_PLAYER, this.onDeath);
    }

    update() {
        this.updateXY();
        super.update();
    }

    updateXY() {
        if (this.targetX < this.x) {
            this.x -= this.speed;
        } else if (this.targetX > this.x) {
            this.x += this.speed;
        }

        if (this.targetY < this.y) {
            this.y -= this.speed;
        } else if (this.targetY > this.y) {
            this.y += this.speed;
        }

        if (Math.abs(this.targetX - this.x) < 0.1 && Math.abs(this.targetY - this.y) < 0.1 && this.isMoving) {
            this.x = this.targetX;
            this.y = this.targetY;
            this.isMoving = false;
            EventManager.Instance.emit(EVENT_TYPE.PLAYER_MOVE_END);
        }
    }

    onAttachShake(type: SHAKE_TYPE_ENUM) {
        EventManager.Instance.emit(EVENT_TYPE.SHAKE_SCREEN, type);
    }

    inputHandle(inputDirection: CONTROLLER_ENUM) {
        if (this.isMoving) {
            return;
        }

        if (this.state === ENTITY_STATE_ENUM.DEATH || this.state === ENTITY_STATE_ENUM.AIR_DEATH || this.state === ENTITY_STATE_ENUM.ATTACH) {
            return;
        }

        const ememyId = this.willAttach(inputDirection);
        if (ememyId) {
            EventManager.Instance.emit(EVENT_TYPE.RECORD_STEP);

            this.state = ENTITY_STATE_ENUM.ATTACH;

            EventManager.Instance.emit(EVENT_TYPE.ATTACH_ENEMY, ememyId);
            EventManager.Instance.emit(EVENT_TYPE.PLAYER_MOVE_END);
            EventManager.Instance.emit(EVENT_TYPE.OPEN_DOOR);
            return;
        }

        if (this.willBlock(inputDirection)) {
            let shakeType: SHAKE_TYPE_ENUM;
            switch (inputDirection) {
                case CONTROLLER_ENUM.UP: {
                    shakeType = SHAKE_TYPE_ENUM.UP;
                    break;
                }
                case CONTROLLER_ENUM.LEFT: {
                    shakeType = SHAKE_TYPE_ENUM.LEFT;
                    break;
                }
                case CONTROLLER_ENUM.RIGHT: {
                    shakeType = SHAKE_TYPE_ENUM.RIGHT;
                    break;
                }
                case CONTROLLER_ENUM.DOWN: {
                    shakeType = SHAKE_TYPE_ENUM.DOWN;
                    break;
                }
                case CONTROLLER_ENUM.TURN_LEFT: {
                    if (this.direction === DIRECTION_ENUM.UP) {
                        shakeType = SHAKE_TYPE_ENUM.LEFT;
                    } else if (this.direction === DIRECTION_ENUM.RIGHT) {
                        shakeType = SHAKE_TYPE_ENUM.UP;
                    } else if (this.direction === DIRECTION_ENUM.DOWN) {
                        shakeType = SHAKE_TYPE_ENUM.RIGHT;
                    } else if (this.direction === DIRECTION_ENUM.LEFT) {
                        shakeType = SHAKE_TYPE_ENUM.DOWN;
                    }
                    break;
                }
                case CONTROLLER_ENUM.TURN_RIGHT: {
                    if (this.direction === DIRECTION_ENUM.UP) {
                        shakeType = SHAKE_TYPE_ENUM.RIGHT;
                    } else if (this.direction === DIRECTION_ENUM.RIGHT) {
                        shakeType = SHAKE_TYPE_ENUM.DOWN;
                    } else if (this.direction === DIRECTION_ENUM.DOWN) {
                        shakeType = SHAKE_TYPE_ENUM.LEFT;
                    } else if (this.direction === DIRECTION_ENUM.LEFT) {
                        shakeType = SHAKE_TYPE_ENUM.UP;
                    }
                    break;
                }
            }
            EventManager.Instance.emit(EVENT_TYPE.SHAKE_SCREEN, shakeType);
            return;
        }

        this.move(inputDirection);
    }

    onDeath(type: ENTITY_STATE_ENUM) {
        this.state = type;
    }

    move(inputDirection: CONTROLLER_ENUM) {
        EventManager.Instance.emit(EVENT_TYPE.RECORD_STEP);
        switch (inputDirection) {
            case CONTROLLER_ENUM.UP: {
                this.targetY -= 1;
                this.isMoving = true;
                EventManager.Instance.emit(EVENT_TYPE.SHOW_SMOKE, this.x, this.y, DIRECTION_ENUM.UP);
                break;
            }
            case CONTROLLER_ENUM.DOWN: {
                this.targetY += 1;
                this.isMoving = true;
                EventManager.Instance.emit(EVENT_TYPE.SHOW_SMOKE, this.x, this.y, DIRECTION_ENUM.DOWN);
                break;
            }
            case CONTROLLER_ENUM.LEFT: {
                this.targetX -= 1;
                this.isMoving = true;
                EventManager.Instance.emit(EVENT_TYPE.SHOW_SMOKE, this.x, this.y, DIRECTION_ENUM.LEFT);
                break;
            }
            case CONTROLLER_ENUM.RIGHT: {
                this.targetX += 1;
                this.isMoving = true;
                EventManager.Instance.emit(EVENT_TYPE.SHOW_SMOKE, this.x, this.y, DIRECTION_ENUM.RIGHT);
                break;
            }
            case CONTROLLER_ENUM.TURN_LEFT: {
                if (this.direction === DIRECTION_ENUM.UP) {
                    this.direction = DIRECTION_ENUM.LEFT;
                } else if (this.direction === DIRECTION_ENUM.LEFT) {
                    this.direction = DIRECTION_ENUM.DOWN;
                } else if (this.direction === DIRECTION_ENUM.RIGHT) {
                    this.direction = DIRECTION_ENUM.UP;
                } else if (this.direction === DIRECTION_ENUM.DOWN) {
                    this.direction = DIRECTION_ENUM.RIGHT;
                }
                this.state = ENTITY_STATE_ENUM.TURN_LEFT;
                EventManager.Instance.emit(EVENT_TYPE.PLAYER_MOVE_END);
                break;
            }
            case CONTROLLER_ENUM.TURN_RIGHT: {
                if (this.direction === DIRECTION_ENUM.UP) {
                    this.direction = DIRECTION_ENUM.RIGHT;
                } else if (this.direction === DIRECTION_ENUM.LEFT) {
                    this.direction = DIRECTION_ENUM.UP;
                } else if (this.direction === DIRECTION_ENUM.RIGHT) {
                    this.direction = DIRECTION_ENUM.DOWN;
                } else if (this.direction === DIRECTION_ENUM.DOWN) {
                    this.direction = DIRECTION_ENUM.LEFT;
                }
                this.state = ENTITY_STATE_ENUM.TURN_RIGHT;
                EventManager.Instance.emit(EVENT_TYPE.PLAYER_MOVE_END);
                break;
            }
        }
    }

    willBlockToTargetByMove(targetState: ENTITY_STATE_ENUM, nextX: number, nextY: number, nextWeaponX: number, nextWeaponY: number, targetX: number, targetY: number) {
        if (targetState !== ENTITY_STATE_ENUM.DEATH) {
            if (nextX === targetX && nextY === targetY || nextWeaponX === targetX && nextWeaponY === targetY) {
                return true;
            }
        }
        return false;
    }

    willBlockToDoorByTurn(targetState: ENTITY_STATE_ENUM, x: number, y: number, nextWeaponX: number, nextWeaponY: number, targetX: number, targetY: number) {
        if (targetState !== ENTITY_STATE_ENUM.DEATH) {
            if (
                x === targetX && nextWeaponY === targetY ||
                nextWeaponX === targetX && y === targetY ||
                nextWeaponX === targetX && nextWeaponY === targetY
            ) {
                return true;
            }
        }
        return false;
    }

    willBlock(inputDirection: CONTROLLER_ENUM) {
        const { targetX: x, targetY: y, direction } = this;
        const { tileInfo } = DataManager.Instance;
        const { x: doorX, y: doorY, state: doorState } = DataManager.Instance.door || {};
        const { mapRowCount: row, mapColumnCount: column } = DataManager.Instance;
        const enemies = DataManager.Instance.enemies.filter(item => item.state !== ENTITY_STATE_ENUM.DEATH);
        const bursts = DataManager.Instance.bursts.filter(item => item.state !== ENTITY_STATE_ENUM.DEATH);

        let nextX: number;
        let nextY: number;
        let nextWeaponX: number;
        let nextWeaponY: number;
        let blockState: ENTITY_STATE_ENUM;
        if (direction === DIRECTION_ENUM.UP) {
            switch (inputDirection) {
                case CONTROLLER_ENUM.UP:
                    nextX = x;
                    nextY = y - 1;
                    nextWeaponX = x;
                    nextWeaponY = y - 2;
                    blockState = ENTITY_STATE_ENUM.BLOCK_FRONT;
                    break;
                case CONTROLLER_ENUM.RIGHT:
                    nextX = x + 1;
                    nextY = y;
                    nextWeaponX = x + 1;
                    nextWeaponY = y - 1;
                    blockState = ENTITY_STATE_ENUM.BLOCK_RIGHT;
                    break;
                case CONTROLLER_ENUM.DOWN:
                    nextX = x;
                    nextY = y + 1;
                    nextWeaponX = x;
                    nextWeaponY = y;
                    blockState = ENTITY_STATE_ENUM.BLOCK_BACK;
                    break;
                case CONTROLLER_ENUM.LEFT:
                    nextX = x - 1;
                    nextY = y;
                    nextWeaponX = x - 1;
                    nextWeaponY = y - 1;
                    blockState = ENTITY_STATE_ENUM.BLOCK_LEFT;
                    break;
                case CONTROLLER_ENUM.TURN_RIGHT:
                    nextWeaponX = x + 1;
                    nextWeaponY = y - 1;
                    blockState = ENTITY_STATE_ENUM.BLOCK_TURN_RIGHT;
                    break;
                case CONTROLLER_ENUM.TURN_LEFT:
                    nextWeaponX = x - 1;
                    nextWeaponY = y - 1;
                    blockState = ENTITY_STATE_ENUM.BLOCK_TURN_LEFT;
                    break;
            }

        } else if (direction === DIRECTION_ENUM.RIGHT) {
            switch (inputDirection) {
                case CONTROLLER_ENUM.UP:
                    nextX = x;
                    nextY = y - 1;
                    nextWeaponX = x + 1;
                    nextWeaponY = y - 1;
                    blockState = ENTITY_STATE_ENUM.BLOCK_LEFT;
                    break;
                case CONTROLLER_ENUM.RIGHT:
                    nextX = x + 1;
                    nextY = y;
                    nextWeaponX = x + 2;
                    nextWeaponY = y;
                    blockState = ENTITY_STATE_ENUM.BLOCK_FRONT;
                    break;
                case CONTROLLER_ENUM.DOWN:
                    nextX = x;
                    nextY = y + 1;
                    nextWeaponX = x + 1;
                    nextWeaponY = y + 1;
                    blockState = ENTITY_STATE_ENUM.BLOCK_RIGHT;
                    break;
                case CONTROLLER_ENUM.LEFT:
                    nextX = x - 1;
                    nextY = y;
                    nextWeaponX = x;
                    nextWeaponY = y;
                    blockState = ENTITY_STATE_ENUM.BLOCK_BACK;
                    break;
                case CONTROLLER_ENUM.TURN_RIGHT:
                    nextWeaponX = x + 1;
                    nextWeaponY = y + 1;
                    blockState = ENTITY_STATE_ENUM.BLOCK_TURN_RIGHT;
                    break;
                case CONTROLLER_ENUM.TURN_LEFT:
                    nextWeaponX = x + 1;
                    nextWeaponY = y - 1;
                    blockState = ENTITY_STATE_ENUM.BLOCK_TURN_LEFT;
                    break;
            }
        } else if (direction === DIRECTION_ENUM.DOWN) {
            switch (inputDirection) {
                case CONTROLLER_ENUM.UP:
                    nextX = x;
                    nextY = y - 1;
                    nextWeaponX = x;
                    nextWeaponY = y;
                    blockState = ENTITY_STATE_ENUM.BLOCK_BACK;
                    break;
                case CONTROLLER_ENUM.RIGHT:
                    nextX = x + 1;
                    nextY = y;
                    nextWeaponX = x + 1;
                    nextWeaponY = y + 1;
                    blockState = ENTITY_STATE_ENUM.BLOCK_LEFT;
                    break;
                case CONTROLLER_ENUM.DOWN:
                    nextX = x;
                    nextY = y + 1;
                    nextWeaponX = x;
                    nextWeaponY = y + 2;
                    blockState = ENTITY_STATE_ENUM.BLOCK_FRONT;
                    break;
                case CONTROLLER_ENUM.LEFT:
                    nextX = x - 1;
                    nextY = y;
                    nextWeaponX = x - 1;
                    nextWeaponY = y + 1;
                    blockState = ENTITY_STATE_ENUM.BLOCK_RIGHT;
                    break;
                case CONTROLLER_ENUM.TURN_RIGHT:
                    nextWeaponX = x - 1;
                    nextWeaponY = y + 1;
                    blockState = ENTITY_STATE_ENUM.BLOCK_TURN_RIGHT;
                    break;
                case CONTROLLER_ENUM.TURN_LEFT:
                    nextWeaponX = x + 1;
                    nextWeaponY = y + 1;
                    blockState = ENTITY_STATE_ENUM.BLOCK_TURN_LEFT;
                    break;
            }
        } else if (direction === DIRECTION_ENUM.LEFT) {
            switch (inputDirection) {
                case CONTROLLER_ENUM.UP:
                    nextX = x;
                    nextY = y - 1;
                    nextWeaponX = x - 1;
                    nextWeaponY = y - 1;
                    blockState = ENTITY_STATE_ENUM.BLOCK_RIGHT;
                    break;
                case CONTROLLER_ENUM.RIGHT:
                    nextX = x + 1;
                    nextY = y;
                    nextWeaponX = x;
                    nextWeaponY = y;
                    blockState = ENTITY_STATE_ENUM.BLOCK_BACK;
                    break;
                case CONTROLLER_ENUM.DOWN:
                    nextX = x;
                    nextY = y + 1;
                    nextWeaponX = x - 1;
                    nextWeaponY = y + 1;
                    blockState = ENTITY_STATE_ENUM.BLOCK_LEFT;
                    break;
                case CONTROLLER_ENUM.LEFT:
                    nextX = x - 1;
                    nextY = y;
                    nextWeaponX = x - 2;
                    nextWeaponY = y;
                    blockState = ENTITY_STATE_ENUM.BLOCK_FRONT;
                    break;
                case CONTROLLER_ENUM.TURN_RIGHT:
                    nextWeaponX = x - 1;
                    nextWeaponY = y - 1;
                    blockState = ENTITY_STATE_ENUM.BLOCK_TURN_RIGHT;
                    break;
                case CONTROLLER_ENUM.TURN_LEFT:
                    nextWeaponX = x - 1;
                    nextWeaponY = y + 1;
                    blockState = ENTITY_STATE_ENUM.BLOCK_TURN_LEFT;
                    break;
            }
        }

        if (inputDirection === CONTROLLER_ENUM.UP || inputDirection === CONTROLLER_ENUM.RIGHT || inputDirection === CONTROLLER_ENUM.DOWN || inputDirection === CONTROLLER_ENUM.LEFT) {
            if (this.willBlockToTargetByMove(doorState, nextX, nextY, nextWeaponX, nextWeaponY, doorX, doorY)) {
                this.state = blockState;
                return true;
            }
            for (let i = 0; i < enemies.length; i += 1) {
                const { x: enemyX, y: enemyY, state: enemyState } = enemies[i];
                if (this.willBlockToTargetByMove(enemyState, nextX, nextY, nextWeaponX, nextWeaponY, enemyX, enemyY)) {
                    this.state = blockState;
                    return true;
                }
            }
            if (nextX < 0 || nextY < 0 || nextX >= row || nextY >= column) {
                this.state = blockState;
                return true;
            }
            const nextTile = tileInfo[nextX]?.[nextY];
            const nextWeaponTile = tileInfo[nextWeaponX]?.[nextWeaponY];

            for (let i = 0; i < bursts.length; i += 1) {
                const { x: burstX, y: burstY } = bursts[i];
                // 移动到地裂上了，而且有枪的位置
                if (nextX === burstX && nextY === burstY && (!nextWeaponTile || nextWeaponTile.turnable)) {
                    return false;
                }
            }

            if (!(nextTile && nextTile.moveable && (!nextWeaponTile || nextWeaponTile.turnable))) {
                this.state = blockState;
                return true;
            }
        } else {
            if (this.willBlockToDoorByTurn(doorState, x, y, nextWeaponX, nextWeaponY, doorX, doorY)) {
                this.state = blockState;
                return true;
            }

            for (let i = 0; i < enemies.length; i += 1) {
                const { x: enemyX, y: enemyY, state: enemyState } = enemies[i];
                if (this.willBlockToDoorByTurn(enemyState, x, y, nextWeaponX, nextWeaponY, enemyX, enemyY)) {
                    this.state = blockState;
                    return true;
                }
            }

            if (
                (!tileInfo[x]?.[nextWeaponY] || tileInfo[x]?.[nextWeaponY].turnable) &&
                (!tileInfo[nextWeaponX]?.[y] || tileInfo[nextWeaponX]?.[y].turnable) &&
                (!tileInfo[nextWeaponX]?.[nextWeaponY] || tileInfo[nextWeaponX]?.[nextWeaponY].turnable)
            ) {
                // 可以转
            } else {
                this.state = blockState;
                return true;
            }
        }

        return false;
    }

    willAttach(inputDirection: CONTROLLER_ENUM) {
        const enemies = DataManager.Instance.enemies.filter(item => item.state !== ENTITY_STATE_ENUM.DEATH);
        for (let i = 0; i < enemies.length; i += 1) {
            const enemy = enemies[i];
            const { x: enemyX, y: enemyY } = enemy;
            if (inputDirection === CONTROLLER_ENUM.UP && this.direction === DIRECTION_ENUM.UP && enemyX === this.x && this.y - 2 === enemyY) {
                return enemy.id;
            }
            if (inputDirection === CONTROLLER_ENUM.LEFT && this.direction === DIRECTION_ENUM.LEFT && enemyY === this.y && this.x - 2 === enemyX) {
                return enemy.id;
            }
            if (inputDirection === CONTROLLER_ENUM.DOWN && this.direction === DIRECTION_ENUM.DOWN && enemyX === this.x && this.y + 2 === enemyY) {
                return enemy.id;
            }
            if (inputDirection === CONTROLLER_ENUM.RIGHT && this.direction === DIRECTION_ENUM.RIGHT && enemyY === this.y && this.x + 2 === enemyX) {
                return enemy.id;
            }
        }

        return '';
    }
}
